<template>
  <div class="container">
    <div class="app-container">
      <!-- 角色管理内容 -->
      <div class="role-btn">
        <el-button size="mini" type="primary" @click="visibleDialog = true">添加角色</el-button>
      </div>
      <!-- 表格 -->
      <!-- 放置table组件 -->
      <!-- highlight-current-row	是否要高亮当前行 -->
      <el-table
        :data="tableData"
        style="width: 100%"
        highlight-current-row
      >
        <!-- 放置列 -->
        <!-- align	对齐方式	String	left/center/right -->
        <el-table-column
          prop="name"
          label="角色"
          width="150"
          align="center"
        >
          <!-- 插槽 -->
          <!-- 3、行内编辑 表格列中根据当前的isEdit标记-渲染结构 通过插槽来实现 -->
          <!-- 条件判断 -->
          <template v-slot="{ row }">
            <el-input v-if="row.isEdit" v-model.trim="row.temEditRow.name" size="mini" />
            <span v-else>{{ row.name }}</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="state"
          label="启用"
          width="150"
          align="center"
        >
          <!-- 自定义列结构 -->
          <!-- 自定义列的内容，参数为 { row, column, $index } -->
          <template v-slot="{row}">
            <el-switch v-if="row.isEdit" v-model="row.temEditRow.state" :active-value="1" :inactive-value="0" size="mini" />
            <span v-else>{{ row.state === 1 ? '已启用' : row.state === 0 ? '未启用' : '无' }}</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="description"
          label="描述"
          align="center"
        >
          <template v-slot="{ row }">
            <el-input v-if="row.isEdit" v-model.trim="row.temEditRow.description" size="mini" :rows="2" type="textarea" placeholder="请输入角色描述" />
            <span v-else>{{ row.description }}</span>
          </template>
        </el-table-column>
        <el-table-column label="操作" align="center">
          <!-- 通过 Scoped slot 可以获取到 row, column, $index 和 store（table 内部的状态管理）的数据。scope对象变量可以随便写，里面的属性字段是固定的 -->
          <!-- 自定义列的内容，参数为 { row, column, $index } -->
          <template #default="scope">
            <!-- 编辑状态 -->
            <template v-if="scope.row.isEdit">
              <el-button
                size="mini"
                type="primary"
                @click="btnEdit(scope.$index, scope.row)"
              >确定</el-button>
              <el-button
                size="mini"
                @click="scope.row.isEdit = false"
              >取消</el-button>
            </template>
            <!-- 非编辑状态 -->
            <template v-else>
              <el-button size="medium" type="text" @click="btnPermission(scope.row.id)">分配权限</el-button>
              <el-button
                size="medium"
                type="text"
                @click="handleEdit(scope.$index, scope.row)"
              >编辑</el-button>
              <!-- confirm事件	点击确认按钮时触发 -->
              <!-- 注意：这里element-ui文档有点问题，el-popconfirm的确认事件是onConfirm，但是文档上写的是confirm事件，这里需要注意 -->
              <el-popconfirm
                confirm-button-text="好的"
                cancel-button-text="不用了"
                icon="el-icon-info"
                icon-color="red"
                title="这条数据确定删除吗？"
                @onConfirm="handleConfirm(scope.row)"
              >
                <!-- 插槽 reference	触发 Popconfirm(气泡确认框) 显示的 HTML 元素 -->
                <!-- <el-button
                  slot="reference"
                  style="margin-left:10px"
                  size="medium"
                  type="text"
                  @click="handleDelete(scope.$index, scope.row)"
                >删除</el-button> -->
                <!-- slot="reference" 使用v-slot:reference替代 简写#reference -->
                <template #reference>
                  <el-button
                    style="margin-left:10px"
                    size="medium"
                    type="text"
                    @click="handleDelete(scope.$index, scope.row)"
                  >删除</el-button>
                </template>
              </el-popconfirm>
            </template>
          </template>
        </el-table-column>
      </el-table>
      <!-- current-page	当前页数，支持 .sync 修饰符 -->
      <!-- 使用Layout flex布局 -->
      <el-row style="height:60px" type="flex" align="middle" justify="end">
        <!-- 放置分页组件 -->
        <el-pagination
          :current-page="page"
          :page-sizes="[2, 5, 10, 20, 50, 100]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
      </el-row>
    </div>
    <!-- 弹框 角色添加 -->
    <!-- .sync修饰符其实就是子组件修改父组件prop时v-bind+v-on+this.$emit的简写 -->
    <!--
      vue修饰符sync的功能是:当一个子组件需要改变了一个 prop 的值时，会通知其父组件进行同步的修改，这个变化也会同步到父组件中所绑定。
      它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。
      在父子组件通信中，有时候需要在父子组件之间共享一个属性，但是 provide/inject 方法不能同步子组件的修改到父组件，
      v-model 又会造成父子组件对属性的修改没有区别，无法区分是谁做出的修改，此时就需要通过 .sync 修饰符来在父子组件之间共享属性
    -->
    <el-dialog
      :visible.sync="visibleDialog"
      title="新增角色"
      width="30%"
      @close="close"
    >
      <!-- 表单内容 -->
      <el-form ref="dataForm" :model="dataForm" :rules="rules" label-width="150px">
        <el-form-item label="角色名称" prop="name">
          <el-input v-model.trim="dataForm.name" size="mini" style="width: 80%;" placeholder="请输入角色名称" />
        </el-form-item>
        <!-- 如果不需要校验 就不需要写 prop属性  但是重置表单(resetFields()方法)时需要prop属性，所以prop="state"属性一定要写的 -->
        <el-form-item label="启用" prop="state">
          <el-switch v-model="dataForm.state" size="mini" :active-value="1" :inactive-value="0" />
        </el-form-item>
        <el-form-item label="角色描述" prop="description">
          <!-- :rows="3" 不加: 代表是字符串3 加上: 代表是数字3 -->
          <el-input v-model.trim="dataForm.description" size="mini" style="width: 80%;" :rows="3" type="textarea" placeholder="请输入角色描述" />
        </el-form-item>
        <el-form-item>
          <!-- flex布局 -->
          <el-row type="flex" align="middle" justify="center">
            <el-col :span="12">
              <el-button size="mini" type="primary" @click="btnOk">确认</el-button>
              <el-button size="mini" @click="close">取消</el-button>
            </el-col>
          </el-row>
        </el-form-item>
      </el-form>
    </el-dialog>
    <!-- 放置权限弹层 -->
    <!-- 分配权限弹框 -->
    <el-dialog title="分配权限" :visible.sync="permissionDialog" width="40%">
      <!-- 放置权限数据 -->
      <!--
        default-checked-keys	默认勾选的节点的 key 的数组
        node-key	每个树节点用来作为唯一标识的属性，整棵树应该是唯一的
        default-checked-keys属性是设置当前选中的节点，但是必须配合node-key属性，因为permIds变量中存储的都是id，必须el-tree组件知道key是哪个字段，所以设置node-key="id"
      -->
      <!-- check-strictly	在显示复选框的情况下，是否严格的遵循父子不互相关联的做法，默认为 false -->
      <el-tree
        ref="permTree"
        :data="permissionList"
        :props="{label:'name'}"
        default-expand-all
        show-checkbox
        node-key="id"
        :default-checked-keys="permIds"
        check-strictly
      />
      <div slot="footer" class="dialog-footer">
        <el-button size="mini" type="primary" @click="btnPermissionOk">确 定</el-button>
        <el-button size="mini" @click="permissionDialog = false">取 消</el-button>
      </div>
      <!-- <el-row slot="footer" type="flex" justify="center">
        <el-col :span="6">
          <el-button type="primary" size="mini" @click="btnPermissionOK">确定</el-button>
          <el-button size="mini" @click="permissionDialog = false">取消</el-button>
        </el-col>
      </el-row> -->
    </el-dialog>
  </div>
</template>
<script>
import { getPermissionList } from '@/api/permission'
import { addRole, getRoleList, updRole, delRole, getRoleInfo, addRolePermission } from '@/api/role'
import { arrayToTree } from '@/utils'

export default {
  name: 'Role',
  data() {
    return {
      // 当前第几页
      page: 1,
      // 每页多少条
      pageSize: 2,
      tableData: [],
      total: 0,
      visibleDialog: false,
      permissionDialog: false,
      permissionList: [],
      currentRoleId: null,
      permIds: [],
      dataForm: {
        name: '',
        // 1启用  0未启用
        state: 0,
        description: ''
      },
      rules: {
        name: [
          { required: true, message: '请输入角色名称', trigger: 'blur' },
          // 自定义校验模式
          {
            trigger: 'blur',
            validator: async(rule, value, callback) => {
              const { rows } = await getRoleList({ page: this.page, pagesize: 99999999 })

              if (rows.some(item => item.name === value)) {
                callback(new Error('已存在角色名称'))
              } else {
                callback()
              }
            }
          }
        ],
        description: [{ required: true, message: '请输入角色描述', trigger: 'blur' }]
      }
    }
  },
  created() {
    this.getRoleList()
  },
  methods: {
    // 点击确定时触发
    async btnPermissionOk() {
      // getCheckedKeys	若节点可被选择（即 show-checkbox 为 true），则返回目前被选中的节点的 key 所组成的数组	(leafOnly) 接收一个 boolean 类型的参数，若为 true 则仅返回被选中的叶子节点的 keys，默认值为 false
      await addRolePermission({ id: this.currentRoleId, permIds: this.$refs.permTree.getCheckedKeys() })
      this.$message.success('分配权限成功')
      this.permissionDialog = false
    },
    async btnPermission(id) {
      this.permissionList = arrayToTree(await getPermissionList(), 0)
      this.currentRoleId = id
      // 查询角色详情
      const { permIds } = await getRoleInfo(id)
      this.permIds = permIds
      this.permissionDialog = true
    },
    async btnEdit(index, row) {
      // 校验
      if (row.temEditRow.name && row.temEditRow.description) {
        // 调用接口
        await updRole({ ...row.temEditRow, id: row.id })
        this.$message.success('更新角色成功')
        // 更新渲染数据 退出编辑状态
        // ESLint 误判 报错
        // row.name = row.temEditRow.name
        // 对象合并、对象浅拷贝 相同属性会覆盖
        // Object.assign(target, source)
        // 规避eslint的误判
        Object.assign(row, {
          ...row.temEditRow,
          // 退出编辑模式
          isEdit: false
        })
      } else {
        this.$message.warning(`第 ${index + 1} 行数据的角色名称或描述不能为空`)
      }
    },
    async getRoleList() {
      const { rows, total } = await getRoleList({ page: this.page, pagesize: this.pageSize })
      this.tableData = rows
      this.total = total
      // 1、行内编辑 针对每一行数据添加一个编辑标记
      this.tableData.forEach(item => {
        // item.isEdit = false // 添加一个属性 初始值为false
        // 数据响应式的问题  数据响应式是数据变化 视图更新
        // 添加的动态属性 不具备响应式特点
        // this.$set(目标对象, 属性名称, 初始值) 可以针对目标对象 添加的属性 添加响应式的属性
        // 为什么不使用item.isEdit = false , 因为动态添加的属性不具备响应式的特点，如果想要具备响应式，可以使用$set
        // > $set的应用
        // - this.$set(目标对象, 属性名称, 初始值 )
        // - 等价于 Vue.set(目标对象, 属性名称, 初始值)
        // - 向响应式对象中添加一个属性，并确保这个新属性同样是响应式的，且触发视图更新。
        this.$set(item, 'isEdit', false)
        // 编辑临时存储对象
        // 因为编辑时，可以取消会滚到之前的状态，所以编辑时的数据是临时的数据
        this.$set(item, 'temEditRow', {
          name: item.name,
          state: item.state,
          description: item.description
        })
      })
    },
    handleEdit(index, row) {
      console.log(index, row)
      // 改变行的编辑状态
      // 2、行内编辑 点击编辑修改编辑标记为true
      row.isEdit = true
      // 因为编辑时，可以取消会滚到之前的状态，所以编辑时的数据是临时的数据
      // 点击编辑 重新更新 编辑临时存储对象
      row.temEditRow.name = row.name
      row.temEditRow.state = row.state
      row.temEditRow.description = row.description
    },
    handleDelete(index, row) {
      console.log(index, row)
    },
    // size-change	pageSize 改变时会触发
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`)
      // 重置当前页
      this.page = 1
      // 改变每页显示多少条
      this.pageSize = val
      // 重新渲染
      this.getRoleList()
    },
    // 切换分页时 请求新的数据
    // current-change	currentPage 改变时会触发
    // val 新的要赋值的当前页码
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`)
      // 改变当前页码
      this.page = val
      // 重新渲染
      this.getRoleList()
    },
    close() {
      // 重置表单
      this.$refs.dataForm.resetFields()
      // 隐藏弹框
      this.visibleDialog = false
    },
    btnOk() {
      // 校验
      this.$refs.dataForm.validate(async isOk => {
        if (isOk) {
          // 调用接口
          await addRole(this.dataForm)

          // 提示信息
          this.$message.success('添加成功')
          // 重新渲染
          this.getRoleList()
          // 重置表单和隐藏弹框
          this.close()
        }
      })
    },
    async handleConfirm(row) {
      await delRole(row.id)
      this.$message.success('删除角色成功')
      // 判断不是第一页 是这页的最后一条数据时 才当前页 - 1
      if (this.page > 1 && this.tableData.length === 1) this.page--
      this.getRoleList()
    }
  }
}
</script>
<style scoped>
.role-btn{
  padding: 15px;
}
</style>
